package com.javashizhan.demo.restclient.client.core;


import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.StringUtils;


class RestClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {

	private ResourceLoader resourceLoader;

	private Environment environment;

	RestClientsRegistrar() {
	}

	@Override
	public void setResourceLoader(ResourceLoader resourceLoader) {
		this.resourceLoader = resourceLoader;
	}

	@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
		registerFeignClients(metadata, registry);
	}


	public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
		ClassPathScanningCandidateComponentProvider scanner = getScanner();
		scanner.setResourceLoader(this.resourceLoader);

		System.out.println("this.resourceLoader: " + this.resourceLoader);


		// 添加一个注解过滤器，有RestClient注解的类/接口才继续处理
		AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(RestClient.class);
		scanner.addIncludeFilter(annotationTypeFilter);

		// 这里的metadata是spring启动类上的注解元数据，下面这一步是获取EnableRestClients注解的属性
		Map<String, Object> attributes = metadata.getAnnotationAttributes(EnableRestClients.class.getName());

		// 得到EnableRestClients注解上的basePackages属性值，只扫描这些包下的class
		Set<String> basePackages = new HashSet<>();

		for (String pkg : (String[]) attributes.get("basePackages")) {
			if (StringUtils.hasText(pkg)) {
				basePackages.add(pkg);
			}
		}

		for (String basePackage : basePackages) {

			System.out.println("basePackage: " + basePackage);

			Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);
			for (BeanDefinition candidateComponent : candidateComponents) {
				if (candidateComponent instanceof AnnotatedBeanDefinition) {

					AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
					// 扫描到的接口/类的注解元数据
					AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();

					// 得到RestClient注解的属性，这里不需要，因为在代理类中可以通过要代理的类的注解获得
//					Map<String, Object> attributes = annotationMetadata
//							.getAnnotationAttributes(RestClient.class.getCanonicalName());

					registerRestClient(registry, annotationMetadata);
				}
			}
		}
	}

	/**
	 * 
	 * @Description: 注册Bean
	 * @param registry
	 * @param annotationMetadata
	 * @param attributes
	 *
	 * @Author 飞流
	 * @Date 2019年8月17日
	 */
	private void registerRestClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata) {
		// 这个类就是扫描到的要处理的类
		String className = annotationMetadata.getClassName();
		BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(RestClientFactoryBean.class);

		// 通过此方式给RestClientFactoryBean的成员赋值，将要实现的类传入
		definition.addPropertyValue("type", className);

		// 设置注入方式
		definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

		AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();

		// 通过RestClientFactoryBean生成指定类的实现，这个类就可以通过@Autowired注入了
		BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className);
		BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
	}

	protected ClassPathScanningCandidateComponentProvider getScanner() {
		return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
			@Override
			protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
				boolean isCandidate = false;
				if (beanDefinition.getMetadata().isIndependent()) {
					if (!beanDefinition.getMetadata().isAnnotation()) {
						isCandidate = true;
					}
				}
				return isCandidate;
			}
		};
	}

	/**
	 * 
	 * @Description: 获取EnableRestClients注解上的basePackages属性
	 * @param importingClassMetadata
	 * @return
	 *
	 * @Author 飞流
	 * @Date 2019年8月17日
	 */
	protected Set<String> getBasePackages(AnnotationMetadata importingClassMetadata) {
		Map<String, Object> attributes = importingClassMetadata
				.getAnnotationAttributes(EnableRestClients.class.getCanonicalName());

		Set<String> basePackages = new HashSet<>();

		for (String pkg : (String[]) attributes.get("basePackages")) {
			if (StringUtils.hasText(pkg)) {
				basePackages.add(pkg);
			}
		}
		return basePackages;
	}

	@Override
	public void setEnvironment(Environment environment) {
		this.environment = environment;
	}

}
